package com.likai.blog.lucene;

import java.io.StringReader;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.SimpleSpanFragmenter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import com.likai.blog.pojo.Blog;

/**
 * 写索引
 * @author likai
 *
 */
public class BlogIndex {

	private Directory dir ;
	
	/**
	 * 获得写索引实例
	 * @return
	 * @throws Exception
	 */
	private IndexWriter getWriter() throws Exception {
		dir = FSDirectory.open(Paths.get("D://lucene")) ;
		
		//中文分词器
		SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer() ;
		
		IndexWriterConfig iwc = new IndexWriterConfig(analyzer) ;
		
		IndexWriter writer = new IndexWriter(dir, iwc) ;
		
		return writer ;
	}
	
	/**
	 * 添加博客索引
	 * @param po
	 * @throws Exception
	 */
	public void addIndex(Blog po) throws Exception {
		IndexWriter writer = getWriter() ;
		
		Document doc = new Document() ;
		//存入id 不分词
		doc.add(new StringField("id",po.getBlogId(),Field.Store.YES));
		//存入title 分词
		doc.add(new TextField("title",po.getTitle(),Field.Store.YES));
		//存入 releaseDate 不分词
		doc.add(new StringField("releaseDate",new SimpleDateFormat("yyyy-MM-dd").format(po.getReleaseDate()),Field.Store.YES));
		//存入没有html标签内容的content 分词
		doc.add(new TextField("content",po.getContentNoTag(),Field.Store.YES));
		
		writer.addDocument(doc);
		
		writer.close();
		
	}
	
	/**
	 * 全文检索博客信息
	 * @param q
	 * @return
	 * @throws Exception
	 */
	public List<Blog> searchBlog(String q) throws Exception {
		//取得路径
		dir = FSDirectory.open(Paths.get("D://lucene")) ;
		//实例化IndexReader
		IndexReader reader = DirectoryReader.open(dir) ;
		//实例化IndexSearcher
		IndexSearcher is = new IndexSearcher(reader) ;
		
		//实现多条件查询(title与content)
		//	实例化BooleanQuery
		BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder() ;
		SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer() ;
		//	实例化QueryParser
		QueryParser parser = new QueryParser("title", analyzer) ;
		Query query = parser.parse(q) ;
		
		QueryParser parser2 = new QueryParser("content", analyzer) ;
		Query query2 = parser2.parse(q) ;
		
		//	添加多条件(或关系)
		booleanQuery.add(query,BooleanClause.Occur.SHOULD) ;
		booleanQuery.add(query2,BooleanClause.Occur.SHOULD) ;
		
		//进行查询
		//	100条最佳得分记录
		TopDocs hits = is.search(booleanQuery.build(), 100);
		//	取得得分片段并关键字高亮显示
		QueryScorer scorer = new QueryScorer(query) ;
		Fragmenter fragmenter = new SimpleSpanFragmenter(scorer) ;
		SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<b><font style='color: red;'>","</font></b>") ;
		Highlighter highlighter = new Highlighter(simpleHTMLFormatter, scorer) ;
		highlighter.setTextFragmenter(fragmenter);
		
		
		List<Blog> blogList = new ArrayList<Blog>() ;
		
		//变量最佳得分记录封装给blogList
		for (ScoreDoc scoreDoc : hits.scoreDocs) {
			//取得Document对象
			Document doc = is.doc(scoreDoc.doc) ;
			Blog blog = new Blog() ;
			blog.setBlogId(doc.get("id"));
			blog.setReleaseDateStr(doc.get("releaseDate"));
			String title = doc.get("title") ;
			//取得content同时对内容中得HTML标签符号进行转义
			String content = StringEscapeUtils.escapeHtml(doc.get("content")) ;
			//设置最佳片段，代码高亮
			if(title != null) {
				//读进去title进行解析
				TokenStream tokenStream = analyzer.tokenStream("title", new StringReader(title)) ;
				//获取已经代码高亮得最佳片段
				String hTitle = highlighter.getBestFragment(tokenStream, title) ;
				//判断是否找到最佳片段
				if(hTitle == null || "".equals(hTitle)) {
					blog.setTitle(title);
				} else {
					blog.setTitle(hTitle);
				}
			}
			
			if(content != null) {
				//读进去content进行解析
				TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(content)) ;
				//获取已经代码高亮得最佳片段
				String hContent = highlighter.getBestFragment(tokenStream, content) ;
				//判断是否找到最佳片段
				if(hContent == null || "".equals(hContent)) {
					if(content.length() <= 200) {
						blog.setContent(content);
					} else {
						blog.setContent(content.substring(0, 200));
					}
				} else {
					blog.setContent(hContent);
				}
			}
			blogList.add(blog) ;
		}
		
		reader.close();
		return blogList ;
	}
	
	/**
	 * 根据key删除博客索引
	 * @param blogId
	 * @throws Exception
	 */
	public void deleteIndex(String blogId) throws Exception {
		IndexWriter writer = getWriter() ;
		
		writer.deleteDocuments(new Term("id",blogId));
		
		//强制删除
		writer.forceMergeDeletes();
		
		writer.commit();
		
		writer.close();
	}
	
	/**
	 * 更新博客索引
	 * @param po
	 * @throws Exception
	 */
	public void updateIndex(Blog po) throws Exception {
		IndexWriter writer = getWriter() ;
		
		Document doc = new Document() ;
		//存入id 不分词
		doc.add(new StringField("id",po.getBlogId(),Field.Store.YES));
		//存入title 分词
		doc.add(new TextField("title",po.getTitle(),Field.Store.YES));
		//存入 releaseDate 不分词
		doc.add(new StringField("releaseDate",new SimpleDateFormat("yyyy-MM-dd").format(new Date()),Field.Store.YES));
		//存入没有html标签内容的content 分词
		doc.add(new TextField("content",po.getContentNoTag(),Field.Store.YES));
		
		writer.updateDocument(new Term("id",po.getBlogId()), doc);
		
		writer.close();
	}
	
}
